﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonTypes.Client
{
    [Serializable]
    public enum CalendarSlotState {FREE, ACKNOWLEDGED, BOOKED, ASSIGNED}

    [Serializable]
    public class CalendarSlot
    {
        private long _slotNumber;
        private CalendarSlotState _slotState;
        private SortedDictionary<long, Reservation> _reservations;
        private SortedDictionary<long, Reservation> _tentativelyBookedReservations;
        private Reservation _votedReservation;
        private Reservation _assignedReservation;
        private bool _hasVotedCommit;
        private Object _lockingObject;

        public CalendarSlot(int slotNumber)
        {
            _slotNumber = slotNumber;
            _slotState = CalendarSlotState.FREE;
            _reservations = new SortedDictionary<long, Reservation>();
            _tentativelyBookedReservations = new SortedDictionary<long, Reservation>();
            _hasVotedCommit = false;

            _lockingObject = new Object();
            _assignedReservation = null;
            _votedReservation = null;
        }

        public long SlotNumber
        {
            get
            {
                lock (_lockingObject)
                { return _slotNumber; }
            }
            set
            {
                lock (_lockingObject)
                { _slotNumber = value; }
            }
        }

        public CalendarSlotState SlotState
        {
            get
            {
                lock (_lockingObject)
                { return _slotState; }
            }
            set
            {
                lock (_lockingObject)
                { _slotState = value; }
            }
        }

        public SortedDictionary<long, Reservation> Reservations
        {
            get
            {
                lock (_lockingObject)
                { return _reservations; }
            }
        }

        public Reservation AssignedReservation
        {
            get
            {
                lock (_lockingObject)
                { return _assignedReservation; }
            }
            set
            {
                lock (_lockingObject)
                { _assignedReservation = value; }
            }
        }

        public Reservation VotedReservation
        {       
            get
            {
                lock (_lockingObject)
                {
                    return _votedReservation;
                }
            }
        }

        public SortedDictionary<long, Reservation> TentativelyBookedReservations
        {
            get
            {
                lock (_lockingObject)
                { return _tentativelyBookedReservations; }
            }
        }
        public bool AddClientReservation(Reservation reservation)
        {
            lock (_lockingObject)
            {
                if (_slotState == CalendarSlotState.FREE || _slotState == CalendarSlotState.ACKNOWLEDGED)
                {
                    _slotState = CalendarSlotState.ACKNOWLEDGED;

                    if (!_reservations.ContainsKey(reservation.TicketNumber))
                        _reservations.Add(reservation.TicketNumber, reservation);
                    else
                        _reservations[reservation.TicketNumber] = reservation;

                    return true;
                }
                else
                    return false;
            }
        }

        public bool AddTentativelyBooked(long ticketNumber)
        {          
            lock (_lockingObject)
            {
                if (_slotState == CalendarSlotState.ASSIGNED)
                    return false;
                else
                {
                    if (!_tentativelyBookedReservations.ContainsKey(ticketNumber))
                        _tentativelyBookedReservations.Add(ticketNumber, _reservations[ticketNumber]);
                    else
                        _tentativelyBookedReservations[ticketNumber] = _reservations[ticketNumber];

                    //if( _reservations[ticketNumber].InvitedClients[0] != _reservations[ticketNumber].InitiatorId)
                        //_reservations.Remove(ticketNumber);

                    _slotState = CalendarSlotState.BOOKED;
                    return true;
                }
            }
        }

        public void RemoveTbReservation(long ticketNumber)
        {
            lock (_lockingObject)
            {
                _tentativelyBookedReservations.Remove(ticketNumber);
                _reservations.Remove(ticketNumber);

                if (_hasVotedCommit && _votedReservation.TicketNumber == ticketNumber)
                {
                    _hasVotedCommit = false;
                    _assignedReservation = null;
                    _votedReservation = null;
                }                          
                if (_tentativelyBookedReservations.Count == 0 && _reservations.Count > 0)
                {
                    _slotState = CalendarSlotState.ACKNOWLEDGED;
                    _hasVotedCommit = false;
                    _assignedReservation = null;
                    _votedReservation = null;
                }
                if (_tentativelyBookedReservations.Count == 0 && _reservations.Count == 0)
                {
                    _slotState = CalendarSlotState.FREE;
                    _hasVotedCommit = false;
                    _assignedReservation = null;
                    _votedReservation = null;
                }
            }
        }

        public bool SlotCommitRequest(long reservationTicket)
        {
            lock (_lockingObject)
            {
                if (_slotState == CalendarSlotState.ASSIGNED)
                    return false;
                else if (_hasVotedCommit && _votedReservation.TicketNumber != reservationTicket)
                {
                    return false;
                }
                else if (_tentativelyBookedReservations.ContainsKey(reservationTicket))
                {                    
                    _hasVotedCommit = true;
                    _votedReservation = _tentativelyBookedReservations[reservationTicket];
                    //_tentativelyBookedReservations.Remove(reservationTicket);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        public bool SlotDoCommit(long reservationTicket)
        {
            lock (_lockingObject)
            {
                if (_slotState == CalendarSlotState.ASSIGNED && _assignedReservation.TicketNumber != reservationTicket)
                {
                    return false;
                }
                else if (_hasVotedCommit && _votedReservation.TicketNumber == reservationTicket)
                {
                    _slotState = CalendarSlotState.ASSIGNED;
                    _assignedReservation = _votedReservation;
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }      
    }
}
